C/C++ HOOK 全局 API 您所在的位置:网站首页 hook api源码 C/C++ HOOK 全局 API

C/C++ HOOK 全局 API

2024-01-25 18:40| 来源: 网络整理| 查看: 265

全局 Hook 不一定需要用到 Dll ,比如全局的鼠标钩子、键盘钩子都是不需要 Dll 的,但是要钩住 API,就需要 Dll 的协助了,下面直接放上 Dll 的代码,注意这里使用的是 MFC DLL。

// Test_Dll(mfc).cpp : 定义 DLL 的初始化例程。 // #include "stdafx.h" #include "Test_Dll(mfc).h" #ifdef _DEBUG #define new DEBUG_NEW #endif #pragma region 我的代码 #define UM_WNDTITLE WM_USER+100 // 自定义消息(私有窗口类的消息标识符) // 全局共享变量(多进程之间共享数据) #pragma data_seg(".Share") HWND g_hWnd = NULL; // 主窗口句柄 HHOOK hhk = NULL; // 鼠标钩子句柄 HINSTANCE hInst = NULL; // 本dll实例句柄 #pragma data_seg() #pragma comment(linker, "/section:.Share,rws") // 全局变量 HANDLE hProcess=NULL; // 进程句柄 BOOL bIsInjected=FALSE; // 是否注入完成 typedef int (WINAPI *MsgBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType); // 声明一个别名 MsgBoxA typedef int (WINAPI *MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType); // 声明一个别名 MsgBoxW MsgBoxA oldMsgBoxA=NULL; // 保存原函数地址 MsgBoxW oldMsgBoxW=NULL; // 保存原函数地址 FARPROC pfMsgBoxA=NULL; // 指向原函数地址的远指针 FARPROC pfMsgBoxW=NULL; // 指向原函数地址的远指针 BYTE OldCodeA[5]; // 老的系统API入口代码 BYTE NewCodeA[5]; // 要跳转的API代码 (jmp xxxx) BYTE OldCodeW[5]; // 老的系统API入口代码 BYTE NewCodeW[5]; // 要跳转的API代码 (jmp xxxx) int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType); // 我们自己的 MessageBoxA 函数 int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType); // 我们自己的 MessageBoxW 函数 // 开启钩子(修改 API 头 5 个字节) void HookOn() { // 检验进程句柄是否为空 ASSERT(hProcess!=NULL); DWORD dwTemp = 0, // 修改后的内存保护属性 dwOldProtect, // 之前的内存保护属性 dwRet = 0, // 内存写入成功标志,0不成功、1成功 dwWrite; // 写入进程内存的字节数 // 更改虚拟内存保护 VirtualProtectEx( hProcess, // 进程句柄 pfMsgBoxA, // 指向保护区域地址的指针 5, // 要更改的区域的字节大小 PAGE_READWRITE, // 内存保护类型,PAGE_READWRITE:可读可写 &dwOldProtect // 接收原来的内存保护属性 ); // 判断是否成功写入内存 dwRet = WriteProcessMemory( hProcess, // 进程句柄 pfMsgBoxA, // 指向写入地址的指针 NewCodeA, // 指向存放写入内容的缓冲区指针 5, // 写入字节数 &dwWrite // 接收传输到进程中的字节数 ); if (0==dwRet||0==dwWrite){ TRACE("NewCodeA 写入失败"); // 记录日志信息 } // 恢复内存保护状态 VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp); // 同上,操作剩下的 MessageBoxW VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); dwRet=WriteProcessMemory(hProcess,pfMsgBoxW,NewCodeW,5,&dwWrite); if (0==dwRet||0==dwWrite){TRACE("NewCodeW 写入失败");} VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp); } // 关闭钩子(修改 API 头 5 个字节) void HookOff() { // 检验进程句柄是否为空 ASSERT(hProcess!=NULL); DWORD dwTemp = 0, // 修改后的内存保护属性 dwOldProtect = 0, // 之前的内存保护属性 dwRet = 0, // 内存写入成功标志,0不成功、1成功 dwWrite = 0; // 写入进程内存的字节数 // 更改虚拟内存保护 VirtualProtectEx( hProcess, // 进程句柄 pfMsgBoxA, // 指向保护区域地址的指针 5, // 要更改的区域的字节大小 PAGE_READWRITE, // 内存保护类型,PAGE_READWRITE:可读可写 &dwOldProtect // 接收原来的内存保护属性 ); dwRet = WriteProcessMemory( hProcess, // 进程句柄 pfMsgBoxA, // 指向写入地址的指针 OldCodeA, // 指向存放写入内容的缓冲区指针 5, // 写入字节数 &dwWrite // 接收传输到进程中的字节数 ); if (0==dwRet||0==dwWrite){ TRACE("OldCodeA 写入失败"); // 记录日志信息 } // 恢复内存保护状态 VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp); // 同上,操作剩下的 MessageBoxW VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); WriteProcessMemory(hProcess,pfMsgBoxW,OldCodeW,5,&dwWrite); if (0==dwRet||0==dwWrite){TRACE("OldCodeW 写入失败");} VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp); } // 代码注入 void Inject() { // 如果还没有注入 if (!bIsInjected){ //保证只调用1次 bIsInjected=TRUE; // 获取函数地址 HMODULE hmod=::LoadLibrary(_T("User32.dll")); oldMsgBoxA=(MsgBoxA)::GetProcAddress(hmod,"MessageBoxA"); // 原 MessageBoxA 地址 pfMsgBoxA=(FARPROC)oldMsgBoxA; // 指向原 MessageBoxA 地址的指针 oldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW"); // 原 MessageBoxW 地址 pfMsgBoxW=(FARPROC)oldMsgBoxW; // 指向原 MessageBoxW 地址的指针 // 指针为空则结束运行 if (pfMsgBoxA==NULL){MessageBox(NULL,_T("cannot get MessageBoxA()"),_T("error"),0);return;} if (pfMsgBoxW==NULL){MessageBox(NULL,_T("cannot get MessageBoxW()"),_T("error"),0);return;} // 将原API中的入口代码保存入 OldCodeA[],OldCodeW[] _asm { lea edi,OldCodeA ; 把 OldCodeA 的地址给 edi mov esi,pfMsgBoxA ; 把 MessageBoxA 的地址给 esi cld ; 方向标志位复位 movsd ; 复制双子 movsb ; 复制字节 } _asm { lea edi,OldCodeW ; 以相同的方式操作 MessageBoxW mov esi,pfMsgBoxW cld movsd movsb } // 将原 API 第一个字节改为 jmp NewCodeA[0]=0xe9; NewCodeW[0]=0xe9; // 计算 jmp 后面要跟的地址 _asm { lea eax,MyMessageBoxA ; 将 MyMessageBoxA 的地址给 eax mov ebx,pfMsgBoxA ; 将 MessageBoxA 的地址给 ebx sub eax,ebx ; 计算 jmp 后面要跟的地址 sub eax,5 mov dword ptr [NewCodeA+1],eax } _asm { lea eax,MyMessageBoxW ; 以相同的方式操作 MessageBoxW mov ebx,pfMsgBoxW sub eax,ebx sub eax,5 mov dword ptr [NewCodeW+1],eax } // 开始 Hook HookOn(); } } // 假 MessageBoxA int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType) { int nRet = 0; // 先恢复 Hook,不然会造成死循环 HookOff(); // 检验 MessageBoxA 是否失败(失败返回 0) nRet = ::MessageBoxA(hWnd,"Hook MessageBoxA",lpCaption,uType); //nRet=::MessageBoxA(hWnd,lpText,lpCaption,uType); // 调用原函数(如果你想暗箱操作的话) // 再次 HookOn,否则只生效一次 HookOn(); return nRet; } // 假 MessageBoxW int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType) { int nRet = 0; // 先恢复 Hook,不然会造成死循环 HookOff(); // 检验 MessageBoxW 是否失败(失败返回 0) nRet = ::MessageBoxW(hWnd,_T("Hook MessageBoxW"),lpCaption,uType); //nRet=::MessageBoxW(hWnd,lpText,lpCaption,uType); // 调用原函数(如果你想暗箱操作的话) // 再次 HookOn,否则只生效一次 HookOn(); return nRet; } #pragma endregion #pragma region 忽略掉 // //TODO: 如果此 DLL 相对于 MFC DLL 是动态链接的, // 则从此 DLL 导出的任何调入 // MFC 的函数必须将 AFX_MANAGE_STATE 宏添加到 // 该函数的最前面。 // // 例如: // // extern "C" BOOL PASCAL EXPORT ExportedFunction() // { // AFX_MANAGE_STATE(AfxGetStaticModuleState()); // // 此处为普通函数体 // } // // 此宏先于任何 MFC 调用 // 出现在每个函数中十分重要。这意味着 // 它必须作为函数中的第一个语句 // 出现,甚至先于所有对象变量声明, // 这是因为它们的构造函数可能生成 MFC // DLL 调用。 // // 有关其他详细信息, // 请参阅 MFC 技术说明 33 和 58。 // // CTest_DllmfcApp BEGIN_MESSAGE_MAP(CTest_DllmfcApp, CWinApp) END_MESSAGE_MAP() // CTest_DllmfcApp 构造 CTest_DllmfcApp::CTest_DllmfcApp() { // TODO: 在此处添加构造代码, // 将所有重要的初始化放置在 InitInstance 中 } // 唯一的一个 CTest_DllmfcApp 对象 CTest_DllmfcApp theApp; #pragma endregion // 程序入口 BOOL CTest_DllmfcApp::InitInstance() { CWinApp::InitInstance(); #pragma region 我的代码 // 获取 dll 自身实例句柄 hInst = AfxGetInstanceHandle(); // 获取调用 dll 的进程 ID DWORD dwPid = ::GetCurrentProcessId(); // 获取调用 dll 的进程句柄 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid); // 开始注入 Inject(); #pragma endregion return TRUE; } // 程序出口 int CTest_DllmfcApp::ExitInstance() { // TODO: 在此添加专用代码和/或调用基类 #pragma region 我的代码 // 恢复其他进程的的 API HookOff(); #pragma endregion return CWinApp::ExitInstance(); } #pragma region 我的代码 // 鼠标钩子回调 LRESULT CALLBACK MouseProc( int nCode, // 钩子代号 WPARAM wParam, // 消息标识符 LPARAM lParam // 光标的坐标 ){ if (nCode==HC_ACTION){ // 将钩子所在窗口句柄发给主程序 ::SendMessage( g_hWnd, // 接收消息的窗口句柄 UM_WNDTITLE, // 发送的消息 wParam, // 附加消息信息 (LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd) // 附加消息信息,此处为鼠标所在窗口的窗口句柄 ); /* typedef struct tagMOUSEHOOKSTRUCT { // 传递给 WH_MOUSE 的鼠标事件信息结构体 POINT pt; // 光标的 xy 坐标 HWND hwnd; // 光标对应的窗口句柄 UINT wHitTestCode; // 是否击中 ULONG_PTR dwExtraInfo; // 消息关联 } MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT, *LPMOUSEHOOKSTRUCT; */ } // 讲消息传递给下一个钩子 return CallNextHookEx( hhk, // 钩子句柄,此处为鼠标钩子 nCode, wParam, lParam ); } // 安装钩子 BOOL WINAPI StartHook(HWND hWnd) { // 获取鼠标所在的主窗口句柄 g_hWnd = hWnd; // 获取鼠标钩子句柄 hhk = ::SetWindowsHookEx( WH_MOUSE, // 钩子类型 MouseProc, // 指向回调函数的指针 hInst, // dll句柄,这里为本 dll 的实例句柄 NULL // 表示与所在桌面的所有线程相关联 ); // 判断 SetWindowsHookEx 是否执行成功 if (hhk==NULL){return FALSE;} else{return TRUE;} } // 卸载钩子 VOID WINAPI StopHook() { // 这里只恢复了自身 API HookOff(); if (hhk!=NULL) { // UnHook 鼠标钩子 UnhookWindowsHookEx(hhk); // 卸载 dll FreeLibrary(hInst); } } #pragma endregion

因为这里没法使用代码折叠,所以不太直观,我放一张折叠后的图:

image.png

在 .def 文件中添加导出函数:(一般就在 .cpp 文件的下面)

; Test_Dll(mfc).def : 声明 DLL 的模块参数。 LIBRARY EXPORTS StartHook StopHook ; 此处可以是显式导出

然后开始写调用 Dll 的代码:(这里要用 MFC 项目,因为全局鼠标钩子需要用到 CWnd 中的 m_hWnd)由于我认为大部分的全局 HOOK 需要在隐藏自己然后默默执行,这与 MFC 的窗口交互模式风格相冲突,所以我在这里隐藏了 MFC 的窗口,

具体方法可以参考:https://blog.csdn.net/Simon798/article/details/99063945

HINSTANCE g_hInst; // 全局变量,同 HMODULE void CTest_MFCDlg::HOOK() { // TODO: 在此添加控件通知处理程序代码 // 加载 dll(需要根据自己 dll 的实际路径而定,建议使用相对路径) g_hInst = ::LoadLibrary(_T("E:\\MyFiles\\Programing\\vs2012\\MyPrograms\\Test_Dll(mfc)\\Debug\\Test_Dll(mfc).dll")); // 判断是否加载成功 if (g_hInst==NULL){AfxMessageBox(_T("加载 dll 失败"));} // 声明别名 typedef BOOL (WINAPI* StartHook)(HWND hWnd); // 调用 dll 中的导出函数 StartHook StartHook Hook = (StartHook)::GetProcAddress(g_hInst,"StartHook"); // 判断导出函数是否调用成功 if (Hook==NULL){AfxMessageBox(_T("StartHook 调用失败"));} // 开始 Hook Hook(m_hWnd); } void CTest_MFCDlg::UNHOOK() { // TODO: 在此添加控件通知处理程序代码 // 检查是否需要 UnHook if (g_hInst==NULL){return;} // 声明别名 typedef VOID (WINAPI* StopHook)(); // 调用 dll 中的导出函数 StopHook StopHook UnHook = (StopHook)::GetProcAddress(g_hInst,"StopHook"); // 判断导出函数是否调用成功 if (UnHook==NULL){AfxMessageBox(_T("StopHook 调用失败"));return;} // 开始 UnHook UnHook(); // 卸载 dll FreeLibrary(g_hInst); // 重置 g_hInst , 方便下一次 UnHook 时判断 g_hInst=NULL; } // 窗体创建事件 int CTest_MFCDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialogEx::OnCreate(lpCreateStruct) == -1) return -1; // TODO: 在此添加您专用的创建代码 // 程序被打开时,执行 HOOK() 函数。这里就不演示 UNHOOK 了。 HOOK(); } void CTest_MFCDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos) { CDialogEx::OnWindowPosChanging(lpwndpos); // TODO: 在此处添加消息处理程序代码 // 隐藏窗体 lpwndpos->flags &= ~SWP_SHOWWINDOW; CDialog::OnWindowPosChanging(lpwndpos); }

效果图:

image.png



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有